#include <xconfig.h>
#include <xcommon.h>

#if CONFIG_WINDOWS
    #include <Windows.h>
    #define __HANDLE__ *((HANDLE *)m_Handle)
#elif CONFIG_LINUX
    #include <pthread.h>
    #include <unistd.h>
    #include <errno.h>
    #include <time.h>
    #define P__HANDLE__ ((ssem_struct *)m_Handle)
#endif
#if CONFIG_BCB
    #pragma hdrstop
#endif
#include "xsemaphore.h"
namespace zdh
{
    #if CONFIG_LINUX
        struct ssem_struct
        {
            int			    semCount;
            int             semMax;
            pthread_mutex_t	mutex;
            pthread_cond_t	condition;
        };
    #endif
    XSemaphore::XSemaphore( int aInit,int aMax )
        :m_Handle(NULL)
    {
        assert( aMax > 0 );
        assert( aInit >=0 && aInit <= aMax );
        #if CONFIG_WINDOWS
            m_Handle = new HANDLE();
            __HANDLE__ = CreateSemaphore(NULL,aInit,aMax,NULL);
        #elif CONFIG_LINUX
            m_Handle = new ssem_struct();
            int iRet = 0;
            iRet = pthread_mutex_init(&P__HANDLE__->mutex, NULL);
            iRet =pthread_cond_init(&P__HANDLE__->condition,NULL);
            P__HANDLE__->semMax = aMax;
            P__HANDLE__->semCount = aInit;
        #endif
    }

    XSemaphore::~XSemaphore()
    {
        #if CONFIG_WINDOWS
            CloseHandle(__HANDLE__);
            delete ((HANDLE *)m_Handle);
            m_Handle = NULL;
        #elif CONFIG_LINUX
            pthread_mutex_destroy(&P__HANDLE__->mutex);
            pthread_cond_destroy(&P__HANDLE__->condition);
            delete P__HANDLE__;
        #endif
    }
    int XSemaphore::WaitForObject()
    {
        int iRet = SEM_WAIT_ABANDONED;
        #if CONFIG_WINDOWS
            switch(WaitForSingleObject(__HANDLE__,INFINITE))
            {
            case WAIT_OBJECT_0:
                iRet = SEM_WAIT_OBJECT;
                break;
            }
        #elif CONFIG_LINUX
        pthread_mutex_lock(&P__HANDLE__->mutex);
        if( P__HANDLE__->semCount <= 0 )
        {
            switch( pthread_cond_wait(&P__HANDLE__->condition,&P__HANDLE__->mutex) )
            {
            case 0:
                iRet = SEM_WAIT_OBJECT;
                P__HANDLE__->semCount --;
                break;
            }
        }
        else
        {
            iRet = SEM_WAIT_OBJECT;
            P__HANDLE__->semCount --;
        }
        if( P__HANDLE__->semCount < 0 ) P__HANDLE__->semCount = 0;
        pthread_mutex_unlock(&P__HANDLE__->mutex);
        #endif
        return iRet;        
    }
    int XSemaphore::WaitForObject(XDWord dwTimeout)
    {
        int iRet = SEM_WAIT_ABANDONED;
        #if CONFIG_WINDOWS
            switch(WaitForSingleObject(__HANDLE__,dwTimeout))
            {
            case WAIT_TIMEOUT:
                iRet = SEM_WAIT_TIME_OUT;
                break;
            case WAIT_OBJECT_0:
                iRet = SEM_WAIT_OBJECT;
                break;
            }
        #elif CONFIG_LINUX
            pthread_mutex_lock(&P__HANDLE__->mutex);
            if( P__HANDLE__->semCount <= 0 )
            {
                timespec aTimeout;
                aTimeout.tv_sec = time(NULL) + dwTimeout / 1000;
                aTimeout.tv_nsec = dwTimeout % 1000;
                switch( pthread_cond_timedwait(&P__HANDLE__->condition,&P__HANDLE__->mutex,&aTimeout) )
                {
                case ETIMEDOUT:
                    iRet = SEM_WAIT_TIME_OUT;
                    break;
                case 0:
                    iRet = SEM_WAIT_OBJECT;
                    P__HANDLE__->semCount --;
                    break;
                }
            }
            else
            {
                iRet = SEM_WAIT_OBJECT;
                P__HANDLE__->semCount --;
            }
            if( P__HANDLE__->semCount < 0 ) P__HANDLE__->semCount = 0;
            pthread_mutex_unlock(&P__HANDLE__->mutex);
        #endif
        return iRet;
    }

    bool XSemaphore::ReleaseCount( int iCount /*= 1*/ )
    {
        assert(iCount>0);
        #if CONFIG_WINDOWS
            return ReleaseSemaphore(__HANDLE__,iCount,NULL) != 0;
        #elif CONFIG_LINUX
            pthread_mutex_lock(&P__HANDLE__->mutex);
            P__HANDLE__->semCount += iCount;
            if( P__HANDLE__->semCount > P__HANDLE__->semMax ) P__HANDLE__->semCount = P__HANDLE__->semMax;
            pthread_mutex_unlock(&P__HANDLE__->mutex);
            pthread_cond_signal(&P__HANDLE__->condition);
            return true;
        #endif
    }
}
